<?php

/*
 * Class: Babel_Translation_Task
 * This class contains all the needed information for a Translation task.
 * 
 * @package BABEL
 * @since   BABEL 0.1.0
 * @version $Id: Translation_Tasks.php 766 2008-05-14 08:52:59Z bjori $
 * @copyright Copyright (C) 2008 Hannes Magnusson
 * @copyright Copyright (C) 2008 Frank Bølviken
 * @copyright Copyright (C) 2008 Audun Skjelvan
 * @author  Hannes Magnusson
 * @author 	Frank Bølviken
 * @author 	Audun Skjelvan
 * @license LGPL
 */

class BabelTranslateException extends UnexpectedValueException {
	private $rsl;

	public function __construct($msg, $rsl = null) {
		parent::__construct($msg);
		$this->rsl = $rsl;
	}
	public function getResult() {
		return $this->rsl;
	}
	public function __sleep() {
		return array("message", "line",);
	}
}

/*
 * Babel_Translation_Task
 *
 * This class defines the current translation task the user works with. 
 *
 */

class Babel_Translation_Task {
	const CHANGED_STRING = -1;
	const NO_CHANGE      = 0;
	const NEW_STRING     = 1;
	const EMPTY_STRING	 = 2;

	private $task;
	private $taskid = null;
	private $changelog = null;
	private $translen = 0;
	private $transstr = "";

	private function __construct(array $task) {
		$keys = array("val_id", /*"context",*/ "org_string");
		foreach($keys as $k) {
			if (!array_key_exists($k, $task)) {
				throw new InvalidArgumentException("Missing parameter '$k'");
			}
		}

		$this->task = $task;
	}
	
	/*
	 * This function returns a new task, which the user can then work with. The 
	 * task with the least amount of vote_points gets top priority.
	 *
	 * 3params:
	 * $langid 						- The language_id the user wants a task for. 
	 * $looped(default to FALSE) 	- 
	 * $skip(default to 0) 			- 
	 *
	 * @return instance of Babel_Translation_Task
	 */
	public static function getLeastProgressTask($langid, $looped = false, &$skip = 0) {
		$skip = (int)$skip; // Force integer
		
		/* {{{ If we are in debug mode, don`t lock tasks for 5 minutes */
		if(!_ICE_DEBUG_TRANSLATION) {
			$timelock = BABEL_STATUS_MAXLIFETIME;
		} else {
			$timelock = BABEL_STATUS_DEBUGLIFETIME;
		}
		/* }}} */
		
		/*
		 * SQL statement which returns a new task for the user.
		 *
		 * 4 params:
		 * BABEL_TRASNALTE_STATUS_VOTE 	- The status that defines if the task is 
		 * 								in voting stage
		 * $timelock 					- The variable defining how long the 
		 * 								task is unavailable for other users to work on.
		 * $_SESSION["user"]["user_id"] - The user_id of the logged on user 
		 * $langid  					- The language the user wants to 
		 * 								translate
		 */
		$tasks = PDODB::execQueryFetch("
									SELECT 
										v.value_id AS val_id,
										v.original_string AS org_string,
										t.translation_id AS transid,
										t.vote_points AS v_points,
										o.context AS context
									FROM
										babel_values v,
										babel_translations t,
										babel_originals o
									WHERE 
										t.status = ?
									AND 
										TIMEDIFF(NOW(),t.changed_date) > ?  
									AND 
										t.value_id NOT IN (
														   	SELECT 
														   		value_id 
															FROM 
																babel_translations
															WHERE 
																user_id = ? 
															AND language_id = ?
															)
									AND
										t.translation_id NOT IN (
																SELECT 
																 	translation_id 
																FROM 
																	babel_votes 
																WHERE 
																	user_id = ?
																) 
									AND  
										t.language_id = ? 
									AND 
										v.value_id = t.value_id
									AND
										v.original_id = o.original_id
									ORDER BY vote_points ASC
									LIMIT $skip,1
			", PDO::FETCH_ASSOC, array(BABEL_TRANSLATE_STATUS_VOTE,
					 $timelock, $_SESSION["user"]["user_id"],
					 $langid, $_SESSION["user"]["user_id"], $langid), $stmt
		); // }}}
		
		if (empty($tasks)) {
			if(!$looped && $stmt->rowCount() == 0){
				$skip = 0;
				return Babel_Translation_Task::getLeastProgressTask($langid,true,$skip);
			}
			return false;
		}

		
		// Increase the translate-number (skipped-tasks)
		$skip++;
		
		$task = $tasks[0];
		$task["lang_id"] = $langid;
		
		/* 
		 * SQL statement which updates the timestamp on a translation task
		 * 
		 * 1param:
		 * $task["val_id"] 	- the translation id to update
		 */
		$success = PDODB::runQuery("
			UPDATE
				babel_translations
			SET
				changed_date = NOW()
			WHERE
				translation_id = ?
			", array($task["val_id"]), $stmt
		);
		/* }}} */

		/* {{{ Error handling */
		if (!$success) {
			Babel_Error_Logger::push($stmt->errorInfo());
			throw new BabelTranslateException("Couldn`t update the DB.");
		}
		/* }}} */
		
		return new Babel_Translation_Task($task);
	}
	
	/*
	 * This functions is used to get a task, by a given ID
	 *
	 * 2params:
	 * $langid 	- The language_id of the task wanted
	 * $id 		- The value_id of the task wanted
	 *
	 * @return 	- instance of Babel_Translation_Task
	 */
	public function getTaskByID($langid, $id) {
		
		/*
		 * SQL statement which returns a translation task to be translated.
		 * The ID of the task is identified with a combination of the variables:
		 * $langid
		 * $id
		 * as described over.
		 *
		 * 7 params:
		 * BABEL_TRANSLATE_STATUS_VOTE 		- Defines voting stage
		 * BABEL_STATUS_IN_TRANSLATION 		- Defines translation stage
		 * BABEL_STATUS_MAXLIFETIME 		- Defines how long you have to wait 
		 *                                  to get a task a user has worked with
		 * $_SESSION["user"]["user_id"] 	- The id of the current user
		 * BABEL_TRANSLATE_STATUS_APPROVED 	- Defiens approved stage
		 * $langid 							- The language_id of the task
		 * $id 								- The value_id of the task
		 */
		$task = !PDODB::execQueryFetch("
									SELECT 
										v.value_id AS val_id,
										v.original_string AS org_string,
										t.user_id AS user_id,
										t.translation_id AS transid,
										t.vote_points AS v_points
									FROM
										babel_values v,
										babel_translations t
									WHERE 
										t.status IN (?,?)
									AND 
										TIMEDIFF(NOW(),t.changed_date) > ?  
									AND 
										t.value_id NOT IN (
														   	SELECT 
														   		value_id 
														 	FROM 
																babel_translations 
															WHERE 
																user_id = ? 
															AND 
																language_id = ?
															) 
									AND  
										t.language_id = ? 
									AND 
										v.value_id = t.value_id
									AND
										v.value_id = ?
									ORDER BY vote_points ASC
									LIMIT 1
			", null, array(BABEL_TRANSLATE_STATUS_VOTE,BABEL_STATUS_IN_TRANSLATION,
					 BABEL_STATUS_MAXLIFETIME, $_SESSION["user"]["user_id"],
					 BABEL_TRANSLATE_STATUS_APPROVED, $langid, (int)$id)
		);
		
		/*
		 * If we didnt find any tasks with this ID combination, we throw a new 
		 * Exception.
		 */	
		if ($empty($task)) {
			throw new BabelTranslateException("No such task");
		}
		
		/*
		 * Sets the language id in the task to the current language id, and 
		 * returns the task
		 */
		$task["lang_id"] = $langid;
		return new Babel_Translation_Task($task);
	}

	/* {{{ Resets the time of the released task to 15 minutes back in time */
	public function releaseTask($taskid) {
		
		/*
		 * {{{ The SQL for going back in time
		 * 
		 * usage: HHMMSS
		 * 
		 * get it?
		 * 
		 * 014512 - would be 1 hour, 45 minutes and 12 seconds
		 * 
		 */
		$success = PDODB::runQuery("
			UPDATE
				babel_translations
			SET 
				changed_date = changed_date - 1500
			WHERE 
				translation_id = ?
			", array($taskid), $stmt
		);
		/* }}} */

		/* {{{ Error handling */
		if (!$success) {
			Babel_Error_Logger::push($stmt->errorInfo());
			throw new BabelTranslateException("Couldn`t update the DB.");
		}
		/* }}} */
	}
	
	/*
	 * This function gets the changelog of a translation
	 */
	public function getChangelog() {		
		
		
		/*
		 * SQL statement which returns the changelog of a translation.
		 *
		 * 2params:
		 * $this->task["val_id"] 	- The value_id of the task
		 * $this->task["lang_id"]  	- The language_id of the task
		 */
		$log = PDODB::execQueryFetch("
SELECT
	t.translated_string AS trans_string
FROM
	babel_translations t
WHERE
	t.value_id = ? AND
	t.language_id = ?
ORDER BY
	created DESC
			", PDO::FETCH_COLUMN, array($this->task["val_id"], $this->task["lang_id"])
		);
		// Sets $log object to $this->changelog, and returns the $log object.
		$this->changelog = $log;
		return $log;
	}

	/*
	 * @return the value_id of the current task
	 */
	public function getID() {
		return $this->task["val_id"];
	}

	/*
	 * @return the context of the current task
	 */
	public function getContext() {
		return $this->task["context"];
	}
	/*
	 * @return the original original constant string
	 */
	public function getOrginalString() {
		return $this->task["org_string"];
	}
	
	/*
	 * @return the translatet string of the current task
	 */
	public function getTranslatedString() {
		return $this->task["transstr"];
	}
	/*
	 * @return the translation id of the current task
	 */
	public function getTranslationID() {
		return $this->task["transid"];
	}
	
	/*
	 * Sets the current translated string to a string
	 * 1param:
	 * $str 	- The new translated string
	 */
	public function setTranslatedString($str) {
		if (!is_string($str) || !($len = strlen($str))) {
			throw new UnexpectedValueException("Missing translation");
		}
		$this->translen = $len;
		$this->transstr = $str;
	}
	
	/*
	 * @returns NO_CHANGE of the string has no changes, or CHANGED_STRING if it 
	 * has.
	 */
	public function compareStrings() {
		if (empty($this->changelog)) {
			return self::NEW_STRING;
		}
		$suggestion = current($this->changelog);
		$orglen = strlen($suggestion);

		if ($orglen == 0) {
			return self::NEW_STRING;
		}

		if($this->translen == 0) {
			return self::EMPTY_STRING;
		}
		if ($orglen != $this->translen) {
			return self::CHANGED_STRING;
		}

		return strcasecmp($this->transstr, $suggestion) == 0 ? self::NO_CHANGE : self::CHANGED_STRING;
	}
	/*
	 * Updates the translation with new informarmation
	 *
	 * 3 params:
	 * $userid 	- User id of the translator
	 * $status 	- Status of the translation
	 * $id 		- Value id of the translation
	 */
	public function updateTranslation($userid, $status, $id) {
		if (!is_numeric($userid) || (int)$userid != $userid) {
			throw new UnexpectedValueException("First parameter must be an integer, got: " . gettype($userid) . " (" .$userid. ")");
		}
		if (!is_int($status)) {
			throw new UnexpectedValueException("Second parameter must be an integer");
		}
		
		/*
		 * SQL statement which updates the translation
		 *
		 * 6params:
		 * $status 					- The status of the task
		 * $userid 					- The user id of the current user 
		 * $this->transstr 			- The translated string
		 * $this->task["lang_id"] 	- The language id of the task
		 * $this->task["val_id"] 	- The value id of the task
		 * $id 						- The translation id of the task
		 */
		$failed = !PDODB::execQuery("
									UPDATE
											babel_translations 
									SET 
											status = ?,
											created = NOW(),
											changed_date = NOW(),
											enabled_date = NOW(),
											user_id = ?,
											translated_string = ?,
											language_id = ?,
											value_id = ?
									WHERE 
											translation_id = ?
										
									", array(
											$status,
											$userid,
											$this->transstr,
											$this->task["lang_id"],
											$this->task["val_id"],
											$id,
										), $newid);
										/* }}} */
		// The update failed, we throw a new Exception
		if ($failed) {
			throw new BabelTranslateException("Weird return value");
		}
		$this->task["transid"] = $newid;
	}
	
	/*
	 * Inserts a new translation to the database.
	 *
	 * 2params:
	 * $userid 	- The user id of the current user
	 * $status 	- The status of the translation
	 */	
	public function insertTranslation($userid, $status) {
		if (!is_numeric($userid) || (int)$userid != $userid) {
			throw new UnexpectedValueException("First parameter must be an integer, got: " . gettype($userid) . " (" .$userid. ")");
		}
 	        if (!is_int($status)) {
 	            throw new UnexpectedValueException("Second parameter must be an integer");
 	        }
 	
			/*
			 * SQL statement which inserts a new translation
			 *
			 * 6 params
			 * $status
			 * $userid
			 * $this->transstr
			 * $this->task["lang_id"]
			 * $this->task["val_id"]
			 * BABEL_INITIAL_TRANSLATION_POINTS
			 */
			$failed = !PDODB::execQuery("
 	INSERT INTO
 	    babel_translations (
 	        status,
 	        created,
 	        changed_date,
 	        enabled_date,
 	        user_id,
 	        translated_string,
 	        language_id,
 	        value_id,
 	        vote_points
 	    ) VALUES(
 	        ?,
 	        NOW(),
 	        NOW(),
 	        NOW(),
 	        ?,
 	        ?,
 	        ?,
 	        ?,
 	        ?
 	    )
 	    ", array(
 	            $status,
 	            $userid,
 	            $this->transstr,
 	            $this->task["lang_id"],
 	            $this->task["val_id"],
 	            BABEL_INITIAL_TRANSLATION_POINTS
 	        ), $newid);
 	        /* }}} */
 			
			// If translation failed, we throw a new Exception	
 	        if ($failed) {
 	            throw new BabelTranslateException("Weird return value");
 	        }
 	        $this->task["transid"] = $newid;
	}
 	
	/*
	 * Sets a translation to rejected, when a new translation is made.
	 *
	 * 1param:
	 * $transid 	- Translation id of the translation to change
	 */
	public function deprecateTranslation($transid){
		
		/*
		 * SQL statement which updates and sets a translation to rejected, after 
		 * a new translation of the same constant has been made
		 *
		 * 2params:
		 * $BABEL_TRANSLATE_STATUS_REJECTED 	- Defines the status REJECTED
		 * $transid 							- The translation id of the 
		 * 										translation to change
		 */
		$failed = !PDODB::execQuery("
									UPDATE
											babel_translations 
									SET 
											status = ?
									WHERE 
											translation_id = ?
										
									", array(
											BABEL_TRANSLATE_STATUS_REJECTED,
											$transid
										), $newid);
		/* }}} */
		
		// If the update failed, we throw a new Exception
		if ($failed) {
			throw new BabelTranslateException("Weird return value");
		}
		//$this->task["transid"] = $newid;
	}

	/*
	 * Inserts a new vote on a translation
	 *
	 * 3 params:
	 * $userid 					- The user id of the current user
	 * $value 					- ?
	 * $transid(default to 0) 	- The translation id of the translation task
	 */
	public function vote($userid, $value, $transid = 0) {
		if (!is_numeric($userid) || (int)$userid != $userid) {
			throw new UnexpectedValueException("First parameter must be an integer, got: " . gettype($userid) . " (" .$userid. ")");
		}
		if (!is_int($value)) {
			throw new UnexpectedValueException("Second parameter must be integer, got '$value'");
		}
		if (!is_int($transid)) {
			throw new UnexpectedValueException("Third parameter must be integer, got '$transid'");
		}
		if ($transid == 0) {
			$transid = $this->task["transid"];
		}
		if ($transid == 0 && !is_int($this->task["transid"])) {
			throw new BabelTranslateException("Uninitialized translation ID");
		}
		
		//{{{ Updates the votes field in the babel_translations when a user votes
		$testobject = new TranslationsPoints_Checks();
		$testobject->checkTranslationApproved($transid, $_SESSION['user']['user_id'], $this->task["v_points"], $this->task["rank"]);
		//}}}

	}

	public function __sleep() {
		return array("task", "taskid", "changelog", "translen", "transstr");
	}

}

